package com.changer.common.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;

import com.aliyun.oss.model.*;
import com.changer.framework.util.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.aliyun.oss.OSSClient;
import com.changer.common.service.ImageCompressPolicy;
import com.changer.common.service.OSSService;
import com.changer.framework.util.FileUtils;

/**
 * @author Jiwei Guo
 *
 */
public class AliOSSServiceImpl implements OSSService {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(AliOSSServiceImpl.class);

	private boolean isInternal;

	private String accessKey;

	private String accessKeySecret;

	private String internalEndpoint;

	private String externalEndpoint;

	private String bucketName;

	private OSSClient internalClient;
	
	private OSSClient externalClient;
	
	private ImageCompressPolicy smallImageCompressPolicy;
	
	private ImageCompressPolicy middleImageCompressPolicy;

	@PostConstruct
	public void init() {
		internalClient = new OSSClient(internalEndpoint, accessKey, accessKeySecret);
		externalClient = new OSSClient(externalEndpoint, accessKey, accessKeySecret);
	}

	public String upload(File file) throws Exception {
		doUploadSmall(file);
		doUploadMiddle(file);
		return doUpload(file);
		//return upload(file, false);
	}
	
	public String upload(File file, boolean justUploadCompressed) throws Exception {
		String smallResult = doUploadSmall(file);
		String middleResult = doUploadMiddle(file);
		if(justUploadCompressed){
			return middleResult == null ? smallResult : middleResult;
		} else{
			return doUpload(file);
		}
	}

	public String doUpload(File file,String prefixKey) throws Exception{
		InputStream content = new FileInputStream(file);
		ObjectMetadata meta = new ObjectMetadata();
		meta.setContentLength(file.length());
		if(isInternal()){
			PutObjectResult result = internalClient.putObject(bucketName, prefixKey+"/"+file.getName(), content, meta);
			return result.getETag();
		} else{
			PutObjectResult result = externalClient.putObject(bucketName, prefixKey+"/"+file.getName(), content, meta);
			return result.getETag();
		}
	}

	private String doUpload(File file) throws Exception {
		InputStream content = new FileInputStream(file);
		ObjectMetadata meta = new ObjectMetadata();
		meta.setContentLength(file.length());
		if(isInternal()){
			PutObjectResult result = internalClient.putObject(bucketName, file.getName(), content, meta);
			return result.getETag();
		} else{
			PutObjectResult result = externalClient.putObject(bucketName, file.getName(), content, meta);
			return result.getETag();
		}
	}
	
	private String doUploadSmall(File file) throws Exception{
		String result = null;
		if(this.smallImageCompressPolicy != null){
			LOGGER.debug("start to upload compressed small file of width : " + this.smallImageCompressPolicy.getWidth() + 
					" , height : " + this.smallImageCompressPolicy.getHeight());
			File small = this.smallImageCompressPolicy.compress(file);
			result = doUpload(small);
			small.delete();
		}
		return result;
	}
	
	private String doUploadMiddle(File file) throws Exception{
		String result = null;
		if(this.middleImageCompressPolicy != null){
			LOGGER.debug("start to upload compressed middle file of width : " + this.middleImageCompressPolicy.getWidth() + 
					" , height : " + this.middleImageCompressPolicy.getHeight());
			File middle = this.middleImageCompressPolicy.compress(file);
			result = doUpload(middle);
			middle.delete();
		}
		return result;
	}

	public String getDownloadURLWithSignature(String key) throws Exception {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(new Date());
		calendar.add(Calendar.MINUTE, 60);
		URL url = externalClient.generatePresignedUrl(bucketName, key, calendar.getTime());
		return url.toString();
	}
	
	public String getHttpsDownloadURLWithSignature(String key) throws Exception {
		return getDownloadURLWithSignature(key).replaceFirst(HTTP, HTTPS);
	}
	
	public String getDownloadURL(String key) throws Exception {
		return HTTP + this.bucketName + "." + this.externalEndpoint + "/" + key;
	}
	
	public String getHttpsDownloadURL(String key) throws Exception {
		return HTTPS + this.bucketName + "." + this.externalEndpoint + "/" + key;
	}

	public void deleteFile(String key) throws Exception {
		if(isInternal()){
			internalClient.deleteObject(bucketName, key);
		} else{
			externalClient.deleteObject(bucketName, key);
		}
	}
	
	public void deleteFiles(List<String> keys) throws Exception {
		DeleteObjectsRequest dor = new DeleteObjectsRequest(bucketName);
		List<List<String>> keysList = CollectionUtils.split(keys,DeleteObjectsRequest.DELETE_OBJECTS_ONETIME_LIMIT);
		for (List<String> deleteKeys : keysList){
			dor.setKeys(deleteKeys);
			if(isInternal()){
				internalClient.deleteObjects(dor);
			} else{
				externalClient.deleteObjects(dor);
			}
		}
	}

	public void setAccessKey(String accessKey) {
		this.accessKey = accessKey;
	}

	public void setAccessKeySecret(String accessKeySecret) {
		this.accessKeySecret = accessKeySecret;
	}

	public void setInternalEndpoint(String internalEndpoint) {
		this.internalEndpoint = internalEndpoint;
	}

	public void setExternalEndpoint(String externalEndpoint) {
		this.externalEndpoint = externalEndpoint;
	}

	public boolean isInternal() {
		return isInternal;
	}

	public void setBucketName(String bucketName) {
		this.bucketName = bucketName;
	}

	public void setInternal(boolean isInternal) {
		this.isInternal = isInternal;
	}
	
	public void setSmallImageCompressPolicy(
			ImageCompressPolicy smallImageCompressPolicy) {
		this.smallImageCompressPolicy = smallImageCompressPolicy;
	}

	public void setMiddleImageCompressPolicy(
			ImageCompressPolicy middleImageCompressPolicy) {
		this.middleImageCompressPolicy = middleImageCompressPolicy;
	}
	
	public File download(String key, String downloadPath) throws Exception {
		File download = new File(downloadPath, key);
		if(!download.exists()){
			download.createNewFile();
		}
		OutputStream out = new FileOutputStream(download);
		InputStream is = null;
		if(isInternal()){
			is = this.internalClient.getObject(this.bucketName, key).getObjectContent();
		} else{
			is = this.externalClient.getObject(this.bucketName, key).getObjectContent();
		}
		FileUtils.stream(is, out);
		return download;
	}
	
	public long size(String key) throws Exception {
		if(isInternal()){
			return this.internalClient.getSimplifiedObjectMeta(this.bucketName,key).getSize();
//			return this.internalClient.getObject(this.bucketName, key).getObjectMetadata().getContentLength();
		} else{
			return this.externalClient.getSimplifiedObjectMeta(this.bucketName,key).getSize();
//			return this.externalClient.getObject(this.bucketName, key).getObjectMetadata().getContentLength();
		}
	}
	
	public boolean exist(String key) throws Exception {
		if(isInternal()){
			return this.internalClient.doesObjectExist(this.bucketName, key);
		} else{
			return this.externalClient.doesObjectExist(this.bucketName, key);
		}
	}

	public List<String> getObjectsByPrefix(String keyPrefix) throws Exception{
		List<String> result = new ArrayList<String>();
		ObjectListing objectListing;
		if(isInternal()){
			 objectListing = this.internalClient.listObjects(this.bucketName, keyPrefix);
		}else{
			 objectListing = this.externalClient.listObjects(this.bucketName, keyPrefix);
		}
		List<OSSObjectSummary> sums = objectListing.getObjectSummaries();
		if(!keyPrefix.contains("flatten")&&!keyPrefix.contains("manual")){
			for (OSSObjectSummary s : sums) {
				if(!s.getKey().contains("flatten")&&!s.getKey().contains("manual"))
				result.add(s.getKey());
			}
		}else{
			for (OSSObjectSummary s : sums) {
				result.add(s.getKey());
			}
		}
		return result;
	}

	public static void main(String[] args) throws Exception {
		AliOSSServiceImpl service = new AliOSSServiceImpl();
		service.setAccessKey("kNOVrivgFErHv3mU");
		service.setAccessKeySecret("uGj0a1hPGNadXTIwgAeFKQ9rScCzdf");
		service.setInternalEndpoint("oss-cn-beijing-internal.aliyuncs.com");
		service.setExternalEndpoint("img-cn-beijing.aliyuncs.com");
		service.setBucketName("changer-et");
		service.setInternal(false);
		service.init();
		System.out.println(service.exist("RBUKWV34RKC2WMP1111A_20160422162745414.jpg"));
//		List<String> keys = Arrays.asList("11.jpg", "11_resize.jpg");
//		service.deleteFiles(keys);
//		System.out.println("over");
//		System.out.println(service.upload(new File("d://1.png")));
		//System.out.println(service.getDownloadURL("test1221.JPG"));
	}

}
